home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / sbin / adduser < prev    next >
Encoding:
Text File  |  2010-11-21  |  33.7 KB  |  1,064 lines

  1. #!/usr/bin/perl
  2.  
  3. # adduser: a utility to add users to the system
  4. # addgroup: a utility to add groups to the system
  5.  
  6. # Copyright (C) 1997, 1998, 1999 Guy Maor <maor@debian.org>
  7. # Copyright (C) 1995 Ted Hajek <tedhajek@boombox.micro.umn.edu>
  8. #                     Ian A. Murdock <imurdock@gnu.ai.mit.edu>
  9. # Bugfixes and other improvements Roland Bauerschmidt <rb@debian.org>
  10. # General scheme of the program adapted by the original debian 'adduser'
  11. #  program by Ian A. Murdock <imurdock@gnu.ai.mit.edu>.
  12. #
  13. #    This program is free software; you can redistribute it and/or modify
  14. #    it under the terms of the GNU General Public License as published by
  15. #    the Free Software Foundation; either version 2 of the License, or
  16. #    (at your option) any later version.
  17. #
  18. #    This program is distributed in the hope that it will be useful,
  19. #    but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21. #    GNU General Public License for more details.
  22. #
  23. #    You should have received a copy of the GNU General Public License
  24. #    along with this program; if not, write to the Free Software
  25. #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  26. #
  27. #
  28. ####################
  29. # See the usage subroutine for explanation about how the program can be called
  30. ####################
  31.  
  32. use warnings;
  33. use strict;
  34. use Debian::AdduserCommon;
  35. use Getopt::Long;
  36.  
  37.  
  38. my $version = "3.112+nmu2";
  39.  
  40. ###################
  41. # return values
  42.  
  43. use constant RET_OK => 0; # OK
  44. use constant RET_OBJECT_ALREADY_EXISTS => 1; # the user or group does already exist, so the requested action cannot be performed
  45. use constant RET_INVALID_CHARS_IN_NAME => 1; # the provided name contains invalid characters
  46. use constant RET_ADDUSER_ABORTED => 1;  # the program was aborted (eg via Ctrl+C)
  47. use constant RET_INVALID_CALL => 1; # getopt returned with "false"
  48.  
  49.  
  50.  
  51.  
  52. BEGIN {
  53.     local $ENV{PERL_DL_NONLAZY}=1;
  54.     eval 'use Locale::gettext';
  55.     if ($@) {
  56.     *gettext = sub { shift };
  57.     *textdomain = sub { "" };
  58.     *LC_MESSAGES = sub { 5 };
  59.     }
  60.     eval {
  61.     require POSIX;
  62.     import POSIX qw(setlocale);
  63.     };
  64.     if ($@) {
  65.     *setlocale = sub { return 1 };
  66.     }
  67.     eval {
  68.     require I18N::Langinfo;
  69.     import I18N::Langinfo qw(langinfo YESEXPR NOEXPR);
  70.     };
  71.     if ($@) {
  72.     *langinfo = sub { return shift; };
  73.     *YESEXPR  = sub { "^[yY]" };
  74.     *NOEXPR   = sub { "^[nN]" };
  75.     }
  76. }
  77.  
  78. setlocale(LC_MESSAGES, "");
  79. textdomain("adduser");
  80. my $yesexpr = langinfo(YESEXPR());
  81.  
  82. my %config;            # configuration hash
  83.  
  84. my @defaults = ("/etc/adduser.conf");
  85. my $nogroup_id = getgrnam("nogroup") || 65534;
  86. $0 =~ s+.*/++; 
  87.  
  88. our $verbose = 1;        # should we be verbose?
  89. my $allow_badname = 0;        # should we allow bad names?
  90. my $ask_passwd = 1;        # ask for a passwd? 
  91. my $disabled_login = 0;        # leave the new account disabled?
  92.  
  93. our $configfile = undef;
  94. our $found_group_opt = undef;
  95. our $found_sys_opt = undef;
  96. our $ingroup_name = undef;
  97. our $new_firstuid = undef;
  98. our $new_gecos = undef;
  99. our $new_gid = undef;
  100. our $new_lastuid = undef;
  101. our $new_uid = undef;
  102. our $no_create_home = undef;
  103. our $special_home = undef;
  104. our $special_shell = undef;
  105. our $add_extra_groups = 0;
  106.  
  107. # Global variables we need later
  108. my $existing_user = undef;
  109. my $existing_group = undef;
  110. my $new_name = undef;
  111. my $make_group_also = 0;
  112. my $home_dir = undef;
  113. my $undohome = undef;
  114. my $undouser = undef;
  115. my $undogroup = undef;
  116. my $shell = undef;
  117. my $first_uid = undef;
  118. my $last_uid = undef;
  119. my $dir_mode = undef;
  120. my $perm = undef;
  121.  
  122. our @names;
  123.  
  124. # Parse options, sanity checks
  125. unless ( GetOptions ("quiet|q" => sub { $verbose = 0 },
  126.             "force-badname" => \$allow_badname,
  127.         "help|h" => sub { &usage(); exit RET_OK },
  128.         "version|v" => sub { &version(); exit RET_OK },
  129.         "system" => \$found_sys_opt,
  130.         "group" => \$found_group_opt,
  131.         "ingroup=s" => \$ingroup_name,
  132.         "home=s" => \$special_home,
  133.         "gecos=s" => \$new_gecos,
  134.         "shell=s" => \$special_shell,
  135.         "disabled-password" => sub { $ask_passwd = 0 },
  136.         "disabled-login" => sub { $disabled_login = 1; $ask_passwd = 0 },
  137.         "uid=i" => \$new_uid,
  138.         "firstuid=i" => \$new_firstuid,
  139.         "lastuid=i" => \$new_lastuid,
  140.         "gid=i" => \$new_gid,
  141.         "conf=s" => \$configfile,
  142.         "no-create-home" => \$no_create_home,
  143.             "add_extra_groups" => \$add_extra_groups,
  144.         "debug" => sub { $verbose = 2 } ) ) {
  145.     &usage();
  146.     exit RET_INVALID_CALL;
  147. }
  148.  
  149. # everyone can issue "--help" and "--version", but only root can go on
  150. dief (gtx("Only root may add a user or group to the system.\n")) if ($> != 0);
  151.  
  152. if( defined($configfile) ) { @defaults = ($configfile); }
  153.  
  154. # detect the right mode
  155. my $action = $0 eq "addgroup" ? "addgroup" : "adduser";
  156. if (defined($found_sys_opt)) {
  157.   $action = "addsysuser" if ($action eq "adduser");
  158.   $action = "addsysgroup" if ($action eq "addgroup");
  159. }
  160.  
  161. # explicitly set PATH, because super (1) cleans up the path and makes adduser unusable;
  162. # this is also a good idea for sudo (which doesn't clean up)
  163. $ENV{"PATH"}="/bin:/usr/bin:/sbin:/usr/sbin";
  164. $ENV{"IFS"}=" \t\n";
  165.  
  166. ############################
  167. # checks related to @names #
  168. ############################
  169.  
  170.  
  171. while (defined(my $arg = shift(@ARGV))) {
  172.   push (@names, $arg);
  173. }
  174.  
  175. if ( (! defined $names[0]) || length($names[0]) == 0 || @names > 2) {
  176.     dief (gtx("Only one or two names allowed.\n"));
  177. }
  178.         
  179.  
  180. if (@names == 2) {    # must be addusertogroup
  181.     dief (gtx("Specify only one name in this mode.\n"))
  182.     if ($action eq "addsysuser" || $found_group_opt);
  183.     $action = "addusertogroup";
  184.     $existing_user = shift (@names);
  185.     $existing_group = shift (@names);
  186. }
  187. else { # 1 parameter, must be adduser
  188.     $new_name = shift (@names);
  189. }
  190.  
  191. ###################################
  192. # check for consistent parameters #
  193. ###################################
  194.  
  195. if ($action ne "addgroup" &&
  196.     defined($found_group_opt) +defined($ingroup_name) +defined($new_gid) > 1 ) {
  197.     dief (gtx("The --group, --ingroup, and --gid options are mutually exclusive.\n"));
  198. }
  199.  
  200.  
  201. if ((defined($special_home)) && ($special_home !~ m+^/+ )) {
  202.   dief (gtx("The home dir must be an absolute path.\n"));
  203. }
  204.        
  205. if (defined($special_home) && $verbose) {
  206.     printf gtx("Warning: The home dir %s you specified already exists.\n"),$special_home
  207.       if (!defined($no_create_home) && -d $special_home);
  208.     printf gtx("Warning: The home dir %s you specified can't be accessed: %s\n"), $special_home, $!
  209.       if (defined($no_create_home) && ! -d $special_home);
  210. }
  211.  
  212.  
  213. if ($found_group_opt) {
  214.     if ($action eq "addsysuser") {
  215.     $make_group_also = 1;
  216.     }
  217.     elsif ($found_sys_opt) {
  218.     $action = "addsysgroup";
  219.     }
  220.     else {
  221.     $action = "addgroup";
  222.     }
  223. }
  224.  
  225.  
  226. $ENV{"VERBOSE"} = $verbose;
  227. $ENV{"DEBUG"}   = $verbose;
  228.  
  229.  
  230. # preseed configuration data and then read the config file
  231. preseed_config(\@defaults,\%config);
  232.  
  233. &checkname($new_name) if defined $new_name;
  234. $SIG{'INT'} = $SIG{'QUIT'} = $SIG{'HUP'} = 'handler';
  235.  
  236. #####
  237. # OK, we've processed the arguments.  $action equals one of the following,
  238. # and the appropriate variables have been set:
  239. #
  240. # $action = "adduser"
  241. #    $new_name                - the name of the new user.
  242. #    $ingroup_name | $new_gid - the group to add the user to
  243. #    $special_home, $new_uid, $new_gecos - optional overrides
  244. # $action = "addgroup"
  245. #    $new_name                - the name of the new group
  246. #    $new_gid                 - optional override
  247. # $action = "addsysgroup"
  248. #    $new_name                - the name of the new group
  249. #    $new_gid                 - optional override
  250. # $action = "addsysuser"
  251. #    $new_name                - the name of the new user
  252. #    $make_group_also | $ingroup_name | $new_gid | 0  - which group
  253. #    $special_home, $new_uid, $new_gecos - optional overrides
  254. # $action = "addusertogroup"
  255. #    $existing_user           - the user to be added
  256. #    $existing_group          - the group to add her to
  257. #####
  258.  
  259.  
  260. #################
  261. ## addsysgroup ##
  262. #################
  263. if ($action eq "addsysgroup") {
  264.  
  265.     # Check if requested group already exists and we can exit safely
  266.     my $ret = existing_group_ok($new_name, $new_gid);
  267.  
  268.     if ($ret == 3) {
  269.         print STDERR "$0: " if $verbose;
  270.     printf STDERR (gtx("The group `%s' already exists as a system group. Exiting.\n"), $new_name) if $verbose;
  271.     exit RET_OK;
  272.     }
  273.  
  274.     if ($ret == 1) {
  275.         print STDERR "$0: " if $verbose;
  276.     printf STDERR (gtx("The group `%s' already exists and is not a system group. Exiting.\n"), $new_name) if $verbose;
  277.     exit RET_OBJECT_ALREADY_EXISTS;
  278.     }
  279.  
  280.     if ($ret == 2) {
  281.         print STDERR "$0: " if $verbose;
  282.     printf STDERR (gtx("The group `%s' already exists, but has a different GID. Exiting.\n"), $new_name) if $verbose;
  283.     exit RET_OBJECT_ALREADY_EXISTS;
  284.     }
  285.  
  286.     dief (gtx("The GID `%s' is already in use.\n"),$new_gid)
  287.     if (defined($new_gid) && defined(getgrgid($new_gid)));
  288.  
  289.     if (!defined($new_gid)) {
  290.         $new_gid = &first_avail_gid($config{"first_system_gid"},
  291.                    $config{"last_system_gid"});
  292.         if ($new_gid == -1) {
  293.         print STDERR "$0: ";
  294.         printf STDERR gtx("No GID is available in the range %d-%d (FIRST_SYS_GID - LAST_SYS_GID).\n"),$config{"first_system_gid"},$config{"last_system_gid"};
  295.             dief (gtx("The group `%s' was not created.\n"),$new_name);
  296.         }
  297.     }
  298.  
  299.  
  300.     printf (gtx("Adding group `%s' (GID %d) ...\n"),$new_name,$new_gid) if $verbose;
  301.     &invalidate_nscd("group");
  302.     my $groupadd = &which('groupadd');
  303.     &systemcall($groupadd, '-g', $new_gid, $new_name);
  304.     &invalidate_nscd("group");
  305.     print (gtx("Done.\n")) if $verbose;
  306.     exit RET_OK;
  307. }
  308.  
  309.  
  310. ##############
  311. ## addgroup ##
  312. ##############
  313. if ($action eq "addgroup") {
  314.     dief (gtx("The group `%s' already exists.\n"),$new_name)
  315.     if (defined getgrnam($new_name));
  316.     dief (gtx("The GID `%s' is already in use.\n"),$new_gid)
  317.     if (defined($new_gid) && defined(getgrgid($new_gid)));
  318.     if (!defined($new_gid)) {
  319.         $new_gid = &first_avail_gid($config{"first_gid"},
  320.                    $config{"last_gid"});
  321.  
  322.         if ($new_gid == -1) {
  323.         print STDERR "$0: ";
  324.         printf STDERR gtx("No GID is available in the range %d-%d (FIRST_GID - LAST_GID).\n"),$config{"first_gid"},$config{"last_gid"};
  325.             dief (gtx("The group `%s' was not created.\n"),$new_name);
  326.         }
  327.     }
  328.  
  329.     printf (gtx("Adding group `%s' (GID %d) ...\n"),$new_name,$new_gid) if $verbose;
  330.     &invalidate_nscd("group");
  331.     my $groupadd = &which('groupadd');
  332.     &systemcall($groupadd, '-g', $new_gid, $new_name);
  333.     &invalidate_nscd("group");
  334.     print (gtx("Done.\n")) if $verbose;
  335.     exit RET_OK;
  336. }
  337.  
  338.  
  339. ####################
  340. ## addusertogroup ##
  341. ####################
  342. if ($action eq "addusertogroup") {
  343.     dief (gtx("The user `%s' does not exist.\n"),$existing_user)
  344.     if (!defined getpwnam($existing_user));
  345.     dief (gtx("The group `%s' does not exist.\n"),$existing_group)
  346.     if (!defined getgrnam($existing_group));
  347.     if (&user_is_member($existing_user, $existing_group)) {
  348.     printf gtx("The user `%s' is already a member of `%s'.\n"),
  349.                 $existing_user,$existing_group if $verbose;
  350.     exit RET_OK;        # not really an error
  351.     }
  352.  
  353.     printf gtx("Adding user `%s' to group `%s' ...\n"),$existing_user,$existing_group
  354.     if $verbose;
  355.     &invalidate_nscd();
  356.     my $gpasswd = &which('gpasswd');
  357.     &systemcall($gpasswd, '-a',$existing_user,$existing_group);
  358.     &invalidate_nscd();
  359.     print (gtx("Done.\n")) if $verbose;
  360.     exit RET_OK;
  361. }
  362.  
  363.  
  364. ################
  365. ## addsysuser ##
  366. ################
  367. if ($action eq "addsysuser") {
  368.     if (existing_user_ok($new_name, $new_uid) == 1) {
  369.  
  370.         # a user with this name already exists; it's a problem when it's not a system user
  371.         my $tmp_u = getpwnam($new_name);
  372.         if (($tmp_u >= $config{"first_system_uid"}) and ($tmp_u <= $config{"last_system_uid"})) {
  373.         printf (gtx("The system user `%s' already exists. Exiting.\n"), $new_name) if $verbose;
  374.             exit RET_OK
  375.         }
  376.     warnf (gtx("The user `%s' already exists. Exiting.\n"), $new_name);
  377.     exit RET_OBJECT_ALREADY_EXISTS;
  378.     }
  379.     if (existing_user_ok($new_name, $new_uid) == 2) {
  380.     warnf (gtx("The user `%s' already exists with a different UID. Exiting.\n"), $new_name);
  381.     exit RET_OBJECT_ALREADY_EXISTS;
  382.     }
  383.  
  384.     if (!$ingroup_name && !defined($new_gid) && !$make_group_also) {
  385.       $new_gid = $nogroup_id;
  386.     }
  387.     check_user_group(1);
  388.  
  389.     if (!defined($new_uid) && $make_group_also) {
  390.     $new_uid = &first_avail_uid($config{"first_system_uid"},
  391.                    $config{"last_system_uid"});
  392.         if ($new_uid == -1) {
  393.         print STDERR "$0: ";
  394.         printf STDERR gtx("No UID/GID pair is available in the range %d-%d (FIRST_SYS_UID - LAST_SYS_UID).\n"),$config{"first_system_uid"},$config{"last_system_uid"};
  395.             dief (gtx("The user `%s' was not created.\n"),$new_name);
  396.         }
  397.         $new_gid = &first_avail_gid($config{"first_system_gid"},
  398.                                 $config{"last_system_gid"});
  399.     $ingroup_name = $new_name;
  400.     }
  401.     elsif (!defined($new_uid) && !$make_group_also) {
  402.     $new_uid = &first_avail_uid($config{"first_system_uid"},
  403.                    $config{"last_system_uid"});
  404.         if ($new_uid == -1) {
  405.         print STDERR "$0: ";
  406.         printf STDERR gtx("No UID is available in the range %d-%d (FIRST_SYS_UID - LAST_SYS_UID).\n"),$config{"first_system_uid"},$config{"last_system_uid"};
  407.         dief (gtx("The user `%s' was not created.\n"),$new_name);
  408.         }
  409.         if (defined($new_gid)) { $ingroup_name = getgrgid($new_gid); }
  410.     elsif ($ingroup_name) { $new_gid = getgrnam($ingroup_name); }
  411.     else { dief (gtx("Internal error")); }
  412.     }
  413.     else {
  414.     if (defined($new_gid)) { $ingroup_name = getgrgid($new_gid); }
  415.     elsif ($ingroup_name) { $new_gid = getgrnam($ingroup_name); }
  416.     elsif ($make_group_also){ $new_gid=$new_uid; $ingroup_name=$new_name; }
  417.     else { dief (gtx("Internal error")); }
  418.     }
  419.     printf (gtx("Adding system user `%s' (UID %d) ...\n"),$new_name,$new_uid) if $verbose;
  420.  
  421.     &invalidate_nscd();
  422.     # if we reach this point, and the group does already exist, we can use it.
  423.     if ($make_group_also && !getgrnam($new_name)) {
  424.     printf (gtx("Adding new group `%s' (GID %d) ...\n"),$new_name,$new_gid) if $verbose;
  425.     $undogroup = $new_name;
  426.        my $groupadd = &which('groupadd');
  427.     &systemcall($groupadd, '-g', $new_gid, $new_name);
  428.     &invalidate_nscd("group");
  429.     }
  430.  
  431.     printf gtx("Adding new user `%s' (UID %d) with group `%s' ...\n"),$new_name,$new_uid,$ingroup_name
  432.     if $verbose;
  433.     $home_dir = $special_home || &homedir($new_name, $ingroup_name);
  434.     $shell = $special_shell || '/bin/false';
  435.     $undouser = $new_name;
  436.     my $useradd = &which('useradd');
  437.     &systemcall($useradd, '-d', $home_dir, '-g', $ingroup_name, '-s',
  438.         $shell, '-u', $new_uid, $new_name);
  439.     if(!$disabled_login) {
  440.         my $usermod = &which('usermod');
  441.         &systemcall($usermod, '-p', '*', $new_name);
  442.     }
  443.     my $chage = &which('chage');
  444.     print "$chage -M 99999 $new_name\n" if ($verbose > 1);
  445.     # do _not_ use systemcall() here, since systemcall() dies on
  446.     # non-zero exit code and we need to do special handling here!
  447.     if (system($chage, '-M', '99999', $new_name)) {
  448.     if( ($?>>8) ne 15 ) {
  449.         &cleanup(sprintf((gtx("`%s' returned error code %d. Exiting.\n")), "$chage -M 99999 $new_name", $?>>8))
  450.           if ($?>>8);
  451.         &cleanup(sprintf((gtx("`%s' exited from signal %d. Exiting.\n")), "$chage -M 99999 $new_name", $?&255));
  452.     } else {
  453.         printf (gtx("%s failed with return code 15, shadow not enabled, password aging cannot be set. Continuing.\n"), $chage);
  454.     }
  455.     }
  456.     &invalidate_nscd();
  457.  
  458.     if(defined($new_gecos)) {
  459.     &ch_gecos($new_gecos);
  460.     }
  461.     create_homedir (0);
  462.  
  463.     exit RET_OK;
  464. }
  465.  
  466.  
  467. #############
  468. ## adduser ##
  469. #############
  470. if ($action eq "adduser") {
  471.     if (!$ingroup_name && !defined($new_gid)) {
  472.     if ($config{"usergroups"} =~  /yes/i) { $make_group_also = 1; }
  473.     else { $new_gid = $config{"users_gid"}; }
  474.     }
  475.     check_user_group(0);
  476.     $first_uid = $new_firstuid || $config{"first_uid"};
  477.     $last_uid = $new_lastuid || $config{"last_uid"};
  478.     printf (gtx("Adding user `%s' ...\n"),$new_name) if $verbose;
  479.  
  480.     if (!defined($new_uid) && $make_group_also) {
  481.     $new_uid = &first_avail_uid($first_uid,
  482.                    $last_uid);
  483.                 
  484.         if ($new_uid == -1) {
  485.         print STDERR "$0: ";
  486.             printf STDERR gtx("No UID/GID pair is available in the range %d-%d (FIRST_UID - LAST_UID).\n"),$first_uid,$last_uid;
  487.         dief (gtx("The user `%s' was not created.\n"),$new_name);
  488.         }
  489.     $new_gid = &first_avail_gid($config{"first_gid"}, 
  490.                                 $config{"last_gid"});
  491.     $ingroup_name = $new_name;
  492.     }
  493.     elsif (!defined($new_uid) && !$make_group_also) {
  494.     $new_uid = &first_avail_uid($first_uid,
  495.                    $last_uid);
  496.     if ($new_uid == -1) {
  497.         print STDERR "$0: ";
  498.         printf STDERR gtx("No UID is available in the range %d-%d (FIRST_UID - LAST_UID).\n"),$config{"first_uid"},$config{"last_uid"};
  499.             dief (gtx("The user `%s' was not created.\n"),$new_name);
  500.         }
  501.     if (defined($new_gid)) { $ingroup_name = getgrgid($new_gid); }
  502.     elsif ($ingroup_name) { $new_gid = getgrnam($ingroup_name); }
  503.     else { dief (gtx("Internal error")); }
  504.     }
  505.     else {
  506.     if (defined($new_gid)) { $ingroup_name = getgrgid($new_gid); }
  507.     elsif ($ingroup_name) { $new_gid = getgrnam($ingroup_name); }
  508.     elsif ($make_group_also){ $new_gid=$new_uid; $ingroup_name=$new_name; }
  509.     else { dief (gtx("Internal error")); }
  510.     }
  511.  
  512.     &invalidate_nscd();
  513.     if ($make_group_also) {
  514.     printf (gtx("Adding new group `%s' (%d) ...\n"),$new_name,$new_gid) if $verbose;
  515.     $undogroup = $new_name;
  516.         my $groupadd = &which('groupadd');
  517.     &systemcall($groupadd, '-g', $new_gid, $new_name);
  518.     &invalidate_nscd();
  519.     }
  520.  
  521.     printf gtx("Adding new user `%s' (%d) with group `%s' ...\n"),$new_name,$new_uid,$ingroup_name
  522.     if $verbose;
  523.     $home_dir = $special_home || &homedir($new_name, $ingroup_name);
  524.     $shell = $special_shell || $config{"dshell"};
  525.     $undouser = $new_name;
  526.     my $useradd = &which('useradd');
  527.     &systemcall($useradd, '-d', $home_dir, '-g', $ingroup_name, '-s',
  528.         $shell, '-u', $new_uid, $new_name);
  529.     &invalidate_nscd();
  530.  
  531.     create_homedir (1); # copy skeleton data
  532.  
  533.     # useradd without -p has left the account disabled (password string is '!')
  534.     my $yesexpr = langinfo(YESEXPR());
  535.     if ($ask_passwd) {
  536.     for (;;) {
  537.           my $passwd = &which('passwd');
  538.       # do _not_ use systemcall() here, since systemcall() dies on
  539.       # non-zero exit code and we need to do special handling here!
  540.           system($passwd, $new_name);
  541.       my $ok = $?>>8;
  542.       if ($ok != 0) {
  543.             my $answer;
  544.             # hm, error, should we break now?
  545.         print (gtx("Permission denied\n")) if ($ok == 1);
  546.         print (gtx("invalid combination of options\n")) if ($ok == 2);
  547.         print (gtx("unexpected failure, nothing done\n")) if ($ok == 3);
  548.         print (gtx("unexpected failure, passwd file missing\n")) if ($ok == 4);
  549.         print (gtx("passwd file busy, try again\n")) if ($ok == 5);
  550.         print (gtx("invalid argument to option\n")) if ($ok == 6);
  551.         
  552.         # Translators: [y/N] has to be replaced by values defined in your
  553.         # locale.  You can see by running "locale noexpr" which regular
  554.         # expression will be checked to find positive answer.
  555.         print (gtx("Try again? [y/N] "));
  556.         chop ($answer=<STDIN>);
  557.         last if ($answer !~ m/$yesexpr/o);
  558.       }
  559.       else {
  560.         last; ## passwd ok
  561.       }
  562.     }
  563.     } else {
  564.     if(!$disabled_login) {
  565.            my $usermod = &which('usermod');
  566.         &systemcall($usermod, '-p', '*', $new_name);
  567.     }
  568.     }
  569.  
  570.     if (defined($new_gecos)) {
  571.     &ch_gecos($new_gecos);
  572.     }
  573.     else {
  574.     my $noexpr = langinfo(NOEXPR());
  575.     for (;;) {
  576.            my $chfn = &which('chfn');
  577.         &systemcall($chfn, $new_name);
  578.         # Translators: [y/N] has to be replaced by values defined in your
  579.         # locale.  You can see by running "locale yesexpr" which regular
  580.         # expression will be checked to find positive answer.
  581.         print (gtx("Is the information correct? [Y/n] "));
  582.         chop (my $answer=<STDIN>);
  583.         last if ($answer !~ m/$noexpr/o);
  584.     }
  585.     }
  586.  
  587.     if ( ( $add_extra_groups || $config{"add_extra_groups"} ) && defined($config{"extra_groups"}) ) {
  588.         printf (gtx("Adding new user `%s' to extra groups ...\n"), $new_name);
  589.         foreach my $newgrp ( split ' ', $config{"extra_groups"} ) {
  590.             if (!defined getgrnam($newgrp)) {
  591.                 warnf (gtx("The group `%s' does not exist.\n"),$newgrp);
  592.                 next;
  593.             }
  594.             if (&user_is_member($new_name, $newgrp)) {
  595.                 printf gtx("The user `%s' is already a member of `%s'.\n"),
  596.                         $new_name,$newgrp if $verbose;
  597.                 next;
  598.  
  599.             }
  600.  
  601.             printf gtx("Adding user `%s' to group `%s' ...\n"),$new_name,$newgrp
  602.                 if $verbose;
  603.             &invalidate_nscd();
  604.             my $gpasswd = &which('gpasswd');
  605.             &systemcall($gpasswd, '-M',
  606.                         join(',', get_group_members($newgrp), $new_name),
  607.                         $newgrp);
  608.             &invalidate_nscd();
  609.         }
  610.     }
  611.  
  612.  
  613.     if ($config{"quotauser"}) {
  614.     printf (gtx("Setting quota for user `%s' to values of user `%s' ...\n"), $new_name, $config{quotauser});
  615.     my $edquota = &which('edquota');
  616.     &systemcall($edquota, '-p', $config{quotauser}, $new_name);
  617.     }
  618.  
  619.     &systemcall('/usr/local/sbin/adduser.local', $new_name, $new_uid,
  620.         $new_gid, $home_dir) if (-x "/usr/local/sbin/adduser.local");
  621.     
  622.     exit RET_OK;
  623. }
  624.  
  625. #
  626. # we never go here
  627. #
  628.  
  629.  
  630. # calculate home directory
  631. sub homedir {
  632.     my $dir = $config{"dhome"};
  633.     $dir .= '/' . $_[1] if ($config{"grouphomes"} =~ /yes/i);
  634.     $dir .= '/' . substr($_[0],0,1) if ($config{"letterhomes"} =~ /yes/i);
  635.     $dir .= '/' . $_[0];
  636.     return $dir;
  637. }
  638.  
  639.  
  640. # create_homedir -- create the homedirectory
  641. # parameter 
  642. #   1: $copy_skeleton: 
  643. #     if 0  -> don't copy the skeleton data
  644. #     if 1  -> copy the files in /etc/skel to the newly created home directory
  645. # return values:
  646. #   none
  647. sub create_homedir {
  648.   my ($copy_skeleton) = @_;
  649.  
  650.   if ($no_create_home) {
  651.       printf gtx("Not creating home directory `%s'.\n"), $home_dir if $verbose;
  652.   }
  653.   elsif (-e $home_dir) {
  654.       printf gtx("The home directory `%s' already exists.  Not copying from `%s'.\n"),
  655.       $home_dir,$config{skel} if $verbose && !$no_create_home;
  656.       my @homedir_stat = stat($home_dir);
  657.       my $home_uid = $homedir_stat[4];
  658.       my $home_gid = $homedir_stat[5];
  659.       if (($home_uid != $new_uid) || ($home_gid != $new_gid)) {
  660.       warnf gtx("Warning: The home directory `%s' does not belong to the user you are currently creating.\n"), $home_dir;
  661.       }
  662.       undef @homedir_stat; undef $home_uid; undef $home_gid;
  663.   }
  664.   else {
  665.       printf gtx("Creating home directory `%s' ...\n"),$home_dir if $verbose;
  666.       $undohome = $home_dir;
  667.       &mktree($home_dir) || &cleanup(sprintf(gtx("Couldn't create home directory `%s': %s.\n"), $home_dir, $!));
  668.       chown($new_uid, $new_gid, $home_dir)
  669.       || &cleanup("chown $new_uid:$new_gid $home_dir: $!\n");
  670.       $dir_mode = get_dir_mode($make_group_also);
  671.       chmod ($dir_mode, $home_dir) ||
  672.       &cleanup("chmod $dir_mode $home_dir: $!\n");
  673.  
  674.       if ($config{"skel"} && $copy_skeleton) {
  675.       printf gtx("Copying files from `%s' ...\n"),$config{skel} if $verbose;
  676.       open(my $FIND, "cd $config{skel}; find .  -print |")
  677.           || &cleanup(sprintf(gtx("fork for `find' failed: %s\n"), $!));
  678.       while (<$FIND>) {
  679.           chop;
  680.           next if ($_ eq ".");
  681.           next if ($_ =~ qr/$config{skel_ignore_regex}/ );
  682.           ©_to_dir($config{"skel"}, $_, $home_dir, $new_uid,
  683.                 $new_gid, ($config{"setgid_home"} =~ /yes/i));
  684.       }
  685.       }
  686.   }
  687. }
  688.  
  689. # mktree: create a directory and all parent directories, we don't care about the rights and so on
  690. # parameters:
  691. #   tree: the path 
  692. # return values:
  693. #   none
  694. sub mktree {
  695.     my($tree) = @_;
  696.     my($done, @path);
  697.     my $default_dir_mode = 0755;
  698.  
  699.     $tree =~ s:^/*(.*)/*$:$1:; # chop off leading & trailing slashes
  700.     @path = split(/\//, $tree);
  701.  
  702.     $done = "";
  703.     while (@path) {
  704.     $done .= '/' . shift(@path);
  705.     -d $done || mkdir($done, $default_dir_mode) || return 0;
  706.     }
  707.     return 1;
  708. }
  709.  
  710. # existing_user_ok: check if there's already a user present on the system which satisfies the requirements
  711. # parameter:
  712. #   new_name: the name of the user to check
  713. #   new_uid : the UID of the user
  714. # return values:
  715. #   0 if the the user doesn't exist 
  716. #   1 if the user already exists with the specified uid (or $new_uid wasn't specified)
  717. #   2 if the user already exists, but $new_uid doesn't matches its uid 
  718. sub existing_user_ok {
  719.     my($new_name,$new_uid) = @_;
  720.     my ($dummy1,$dummy2,$uid);
  721.     if (($dummy1,$dummy2,$uid) = getpwnam($new_name)) {
  722.     if( defined($new_uid) && $uid == $new_uid ) {
  723.         return 1;
  724.     }
  725.     if (! defined($new_uid)) { 
  726.         return 1;
  727.     }
  728.         # TODO: do we really need this code? Range check shouldn't performed here
  729.     if( $uid >= $config{"first_system_uid"} &&
  730.         $uid <= $config{"last_system_uid" } ) {
  731.         return 2;
  732.     }
  733.     } else {
  734.     return 0;
  735.     }
  736. }
  737.  
  738. # existing_group_ok: check if there's already a group which satiesfies the requirements
  739. # parameter:
  740. #   new_name: the name of the group
  741. #   new_gid : the UID of the group
  742. # return values:
  743. #   0 if the group doesn't exist
  744. #   1 if the group already exists with the specified gid (or $new_gid wasn't specified)
  745. #   2 if the group already exists, but $new_gid doesn't match its gid 
  746. #   3 if the group already exists inside the system range
  747. sub existing_group_ok {
  748.     my($new_name,$new_gid) = @_;
  749.     my ($dummy1,$dummy2,$gid);
  750.     if (($dummy1,$dummy2,$gid) = getgrnam($new_name)) {
  751.  
  752.         # TODO: is this check required? There shouldn't be any gid outside of our allowed range anyways ...
  753.     if( $gid >= $config{"first_system_gid"} &&
  754.         $gid <= $config{"last_system_gid" } ) {
  755.         return 3;
  756.     }
  757.     if (! defined($new_gid)) {
  758.         return 1;
  759.     }
  760.         if ($gid == $new_gid) {
  761.             return 1;
  762.     } else {
  763.             return 2;
  764.         }
  765.     } else {
  766.     return 0;
  767.     }
  768. }
  769.  
  770.  
  771.  
  772. # check_user_group: ???
  773. # parameters:
  774. #   system: 0 if the user isn't a system user, 1 otherwise
  775. # return values:
  776. #   
  777. sub check_user_group {
  778.     my ($system) = @_;
  779.     if( !$system || !existing_user_ok($new_name, $new_uid) ) {
  780.     if( defined getpwnam($new_name) ) {
  781.         if( $system ) {
  782.         dief (gtx("The user `%s' already exists, and is not a system user.\n"),$new_name);
  783.         } else {
  784.         dief (gtx("The user `%s' already exists.\n"),$new_name);
  785.         }
  786.     }
  787.     dief (gtx("The UID %d is already in use.\n"),$new_uid)
  788.       if (defined($new_uid) && getpwuid($new_uid));
  789.     }
  790.     if ($make_group_also) {
  791.     if( !$system || !existing_group_ok($new_name, $new_uid) ) {
  792.         dief (gtx("The group `%s' already exists.\n"),$new_name)
  793.           if (defined getgrnam($new_name));
  794.         dief (gtx("The GID %d is already in use.\n"),$new_uid)
  795.           if (defined($new_uid) && defined(getgrgid($new_uid)));
  796.     }
  797.     }
  798.     else {
  799.     dief (gtx("The group `%s' does not exist.\n"),$ingroup_name)
  800.         if ($ingroup_name && !defined(getgrnam($ingroup_name)));
  801.     dief (gtx("The GID %d does not exist.\n"),$new_gid)
  802.         if (defined($new_gid) && !defined(getgrgid($new_gid)));
  803.     }
  804. }
  805.  
  806.  
  807. # copy_to_dir :
  808. # parameters:
  809. #   fromdir
  810. #   file
  811. #   todir
  812. #   newi
  813. #   newg
  814. #   sgiddir
  815. # return values:
  816. #   none
  817. sub copy_to_dir {
  818.     my($fromdir, $file, $todir, $newu, $newg, $sgiddir) = @_;
  819.  
  820.     if (-l "$fromdir/$file") {
  821.     my $target=readlink("$fromdir/$file") or &cleanup("readlink: $!\n");
  822.     my $curgid="$)";
  823.     my $curuid="$>";
  824.     my $error="";
  825.     $)="$newg";
  826.     $>="$newu";
  827.     symlink("$target", "$todir/$file") or $error="$!";
  828.         $>="$curuid";
  829.         $)="$curgid";
  830.     if( "$error" ne "" ) {
  831.         &cleanup("symlink: $!\n");
  832.     }
  833.     return;
  834.     }
  835.     elsif (-f "$fromdir/$file") {
  836.     open (FILE, "$fromdir/$file") || &cleanup("open $fromdir/$file: $!");
  837.     open (NEWFILE, ">$todir/$file") || &cleanup("open >$todir/$file: $!");
  838.  
  839.     (print NEWFILE <FILE>) || &cleanup("print $todir/$file: $!");
  840.     close FILE;
  841.     close(NEWFILE)  || &cleanup("close $todir/$file ");
  842.  
  843.     }
  844.     elsif (-d "$fromdir/$file") {
  845.     mkdir("$todir/$file", 700) || &cleanup("mkdir: $!");
  846.     }
  847.     else {
  848.     &cleanup(sprintf((gtx("Cannot deal with %s.\nIt is not a dir, file, or symlink.\n")), "$fromdir/$file"));
  849.     }
  850.     
  851.     chown($newu, $newg, "$todir/$file")
  852.     || &cleanup("chown $newu:$newg $todir/$file: $!\n");
  853.     $perm = (stat("$fromdir/$file"))[2] & 07777;
  854.     $perm |= 02000 if (-d "$fromdir/$file" && ($perm & 010) && $sgiddir);
  855.     chmod($perm, "$todir/$file") || &cleanup("chmod $todir/$file: $!\n");
  856. }
  857.        
  858.  
  859. # checkname: perform some sanity checks
  860. # parameters:
  861. #   none
  862. # return values:
  863. #   none (exits on error)
  864. sub checkname {
  865.     my ($name) = @_;
  866.     if ($name !~ /^[_.A-Za-z0-9][-\@_.A-Za-z0-9]*\$?$/) {
  867.     printf STDERR
  868. (gtx("%s: To avoid problems, the username should consist only of
  869. letters, digits, underscores, periods, at signs and dashes, and not start with
  870. a dash (as defined by IEEE Std 1003.1-2001). For compatibility with Samba
  871. machine accounts \$ is also supported at the end of the username\n"), $0);
  872.         exit RET_INVALID_CHARS_IN_NAME;;
  873.     }
  874.     if ($name !~ qr/$config{"name_regex"}/) {
  875.       if ($allow_badname) {
  876.     print (gtx("Allowing use of questionable username.\n")) if ($verbose);
  877.       }
  878.       else {
  879.         printf STDERR
  880. (gtx("%s: Please enter a username matching the regular expression configured
  881. via the NAME_REGEX configuration variable.  Use the `--force-badname'
  882. option to relax this check or reconfigure NAME_REGEX.\n"), $0);
  883.         exit RET_INVALID_CHARS_IN_NAME;
  884.       }
  885.     }
  886. }
  887.  
  888. # first_avail_uid: return the first available uid in given range
  889. # parameters:
  890. #   min, max: the range
  891. # return values:
  892. #   -1 if no free uid is available
  893. #  otherwise the choosen uid
  894. sub first_avail_uid {
  895.     my ($min, $max) = @_;
  896.     printf (gtx("Selecting UID from range %d to %d ...\n"),$min,$max) if ($verbose > 1);
  897.  
  898.     my $t = $min;
  899.     while ($t <= $max) {
  900.        return $t if (!defined(getpwuid($t)));
  901.        $t++;
  902.     }
  903.     return -1; # nothing available
  904. }
  905.  
  906. # first_avail_gid: return the first available gid in given range
  907. # parameters:
  908. #   min, max: the range
  909. # return values:
  910. #   -1 if no free gid is available
  911. #   otherwise the choosen gid
  912. sub first_avail_gid {
  913.     my ($min, $max) = @_;
  914.     printf (gtx("Selecting GID from range %d to %d ...\n"),$min,$max) if ($verbose > 1);
  915.  
  916.     my $t = $min;
  917.     while ($t <= $max) {
  918.        return $t if (!defined(getgrgid($t)));
  919.        $t++;
  920.     }
  921.     return -1; # nothing available
  922. }
  923.  
  924. sub ch_gecos {
  925.     my $chfn = &which('chfn');
  926.     my $gecos = shift;
  927.     if($gecos =~ /,/)
  928.       {
  929.       my($gecos_name,$gecos_room,$gecos_work,$gecos_home,$gecos_other)
  930.         = split(/,/,$gecos);
  931.  
  932.       &systemcall($chfn, '-f', $gecos_name, '-r', $gecos_room, $new_name);
  933.       &systemcall($chfn,'-w',$gecos_work,$new_name)
  934.         if(defined($gecos_work));
  935.       &systemcall($chfn,'-h',$gecos_home,$new_name)
  936.         if(defined($gecos_home));
  937.       &systemcall($chfn,'-o',$gecos_other,$new_name)
  938.         if(defined($gecos_other));
  939.       }
  940.     else
  941.       {
  942.       &systemcall($chfn, '-f', $gecos, $new_name);
  943.       }
  944. }
  945.  
  946. # user is member of group?
  947. sub user_is_member {
  948.     my($user, $group) = @_;
  949.     for (split(/ /, (getgrnam($group))[3])) {
  950.     return 1 if ($user eq $_);
  951.     }
  952.     return 0;
  953. }
  954.  
  955.  
  956. sub cleanup {
  957.     my ($msg) = @_;
  958.     printf (gtx("Stopped: %s\n"),$msg);
  959.     if ($undohome) {
  960.     printf (gtx("Removing directory `%s' ...\n"),$undohome);
  961.     &systemcall('rm', '-rf', $undohome);
  962.     }
  963.     if ($undouser) {
  964.     printf (gtx("Removing user `%s' ...\n"),$undouser);
  965.     &systemcall('userdel', $undouser);
  966.     }
  967.     if ($undogroup) {
  968.     printf (gtx("Removing group `%s' ...\n"),$undogroup);
  969.     &systemcall('groupdel', $undogroup);
  970.     }
  971.     # do we need to invalidate the nscd cache here, too?
  972.     exit RET_ADDUSER_ABORTED;
  973. }
  974.  
  975. sub handler {
  976.     my($sig) = @_;
  977.     # Translators: the variable %s is INT, QUIT, or HUP.
  978.     # Please do not insert a space character between SIG and %s.
  979.     &cleanup(sprintf(gtx("Caught a SIG%s.\n"), $sig));
  980. }
  981.     
  982.  
  983. sub version {
  984.     printf (gtx("adduser version %s\n\n"), $version);
  985.     print gtx("Adds a user or group to the system.
  986.   
  987. Copyright (C) 1997, 1998, 1999 Guy Maor <maor\@debian.org>
  988. Copyright (C) 1995 Ian Murdock <imurdock\@gnu.ai.mit.edu>,
  989.                    Ted Hajek <tedhajek\@boombox.micro.umn.edu>
  990. \n");
  991.     print gtx(
  992. "This program is free software; you can redistribute it and/or modify
  993. it under the terms of the GNU General Public License as published by
  994. the Free Software Foundation; either version 2 of the License, or (at
  995. your option) any later version.
  996.  
  997. This program is distributed in the hope that it will be useful, but
  998. WITHOUT ANY WARRANTY; without even the implied warranty of
  999. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  1000. General Public License, /usr/share/common-licenses/GPL, for more details.
  1001. ");
  1002. }
  1003.  
  1004. sub usage {
  1005.     printf gtx(
  1006. "adduser [--home DIR] [--shell SHELL] [--no-create-home] [--uid ID]
  1007. [--firstuid ID] [--lastuid ID] [--gecos GECOS] [--ingroup GROUP | --gid ID]
  1008. [--disabled-password] [--disabled-login] USER
  1009.    Add a normal user
  1010.  
  1011. adduser --system [--home DIR] [--shell SHELL] [--no-create-home] [--uid ID]
  1012. [--gecos GECOS] [--group | --ingroup GROUP | --gid ID] [--disabled-password]
  1013. [--disabled-login] USER
  1014.    Add a system user
  1015.  
  1016. adduser --group [--gid ID] GROUP
  1017. addgroup [--gid ID] GROUP
  1018.    Add a user group
  1019.  
  1020. addgroup --system [--gid ID] GROUP
  1021.    Add a system group
  1022.  
  1023. adduser USER GROUP
  1024.    Add an existing user to an existing group
  1025.  
  1026. general options:
  1027.   --quiet | -q      don't give process information to stdout
  1028.   --force-badname   allow usernames which do not match the
  1029.                     NAME_REGEX configuration variable
  1030.   --help | -h       usage message
  1031.   --version | -v    version number and copyright
  1032.   --conf | -c FILE  use FILE as configuration file\n\n");
  1033. }
  1034.  
  1035. sub get_dir_mode
  1036.   {
  1037.       my $setgid = shift;
  1038.       # no longer make home directories setgid per default (closes: #64806)
  1039.       $setgid = 0 unless $config{"setgid_home"} =~  /yes/i;
  1040.  
  1041.       my $dir_mode = $config{"dir_mode"};
  1042.       if(!defined($dir_mode) || ! ($dir_mode =~ /[0-7]{3}/ ||
  1043.                    $dir_mode =~ /[0-7]{4}/))
  1044.     {
  1045.         $dir_mode = $setgid ? 2755 : 0755;
  1046.     }
  1047.       else
  1048.     {
  1049.         $dir_mode = $config{"dir_mode"};
  1050.         if($setgid && (length($dir_mode) == 3 || $dir_mode =~ /^[0-1|4-5][0-7]{3}$/))
  1051.           {
  1052.           $dir_mode += 2000;
  1053.           }
  1054.     }
  1055.       return oct($dir_mode);
  1056.   }
  1057.  
  1058. # Local Variables:
  1059. # mode:cperl
  1060. # cperl-indent-level:4
  1061. # End:
  1062.  
  1063. # vim:set ai et sts=4 sw=4 tw=0:
  1064.